using System;
using FireStarter.Base;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using FireStarterModeller.BO;
using FireStarterModeller.Test;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.Testability;
using NUnit.Framework;
using Rhino.Mocks;

namespace Firestarter.DB_DomainModel.Logic.Test
{
    [TestFixture]
    public class TestDBToDMPropertyUpdater : TestBase
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(BOBroker.GetClassDefsXml(), new DtdLoader()).LoadClassDefs());
        }

        // ReSharper disable RedundantNameQualifier
        [SetUp]
        public virtual void SetupTest()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }
        //Story: There are othere scenarios that need to be tested where the values have already been set on the DMProp

        private static DBToDMPropertyUpdater CreateDBToDMPropertyUpdater()
        {
            IDBTypeToDMPropertyTypeMapper typeMapper = MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>();
            return new DBToDMPropertyUpdater(typeMapper);
        }

        [Test]
        public void Test_Construct()
        {
            //---------------Set up test pack-------------------
            IDBTypeToDMPropertyTypeMapper typeMapper = MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(typeMapper);
            //---------------Test Result -----------------------
            Assert.IsNotNull(dbToDMPropertyUpdater);
            Assert.AreSame(typeMapper, dbToDMPropertyUpdater.DBTypeToDMPropertyTypeMapper);
        }

        [Test]
        public void Test_Construct_WhenNullDBTypeToDMPropertyTypeMapper_ShouldThrowError()
        {
            //---------------Set up test pack-------------------
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                new DBToDMPropertyUpdater(null);
                //---------------Test Result -----------------------
                Assert.Fail("Expected to throw an ArgumentNullException");
            }
            catch (ArgumentNullException ex)
            {
                Assert.AreEqual("typeMapper", ex.ParamName);
            }
        }

        [Test]
        public void Test_UpdateValues_WhenHasLeadingAndTrailingWhiteSpace_ShouldUpdatePropertyName()
        {
            //---------------Set up test pack-------------------
            DMProperty boProp = new DMProperty();
            IDBColumn dbColumn = GetDBColumn("  FieldName  ");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(dbColumn.ColumnName.Trim(), boProp.PropertyName);
        }

        [Test]
        public void Test_UpdateValues_WhenHasWhiteSpaceInFieldName_ShouldUpdatePropertyName()
        {
            //---------------Set up test pack-------------------
            DMProperty boProp = new DMProperty();
            IDBColumn dbColumn = GetDBColumn("  Field  Name  ");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual("FieldName", boProp.PropertyName);
        }

        [Test]
        public void Test_UpdateValues_ShouldUpdatePropertyName_AndDelimitPascalCase()
        {
            //---------------Set up test pack-------------------
            DMProperty boProp = new DMProperty();
            const string tableName = "field_name";
            IDBColumn dbColumn = GetDBColumn(tableName);
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual("FieldName", boProp.PropertyName);
        }

        [Test]
        public void Test_UpdateValues_ShouldUpdateDescriptionFromFieldComment()
        {
            //---------------Set up test pack-------------------
            DMProperty boProp = new DMProperty();
            IDBColumn dbColumn = GetDBColumn("field-name");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            const string fieldComment = "This is what this field is for";
            dbColumn.Comment = fieldComment;
            //---------------Assert Precondition----------------
            Assert.AreEqual(fieldComment, dbColumn.Comment);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(fieldComment, boProp.Description);
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnNotNullable_ShouldUpdateCompulsoryToTrue()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            dbColumn.Nullable = false;
            //---------------Assert Precondition----------------
            Assert.IsFalse(dbColumn.Nullable.Value);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsTrue(boProp.Compulsory.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnNullable_ShouldUpdateCompulsoryToFalse()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            dbColumn.Nullable = true;
            //---------------Assert Precondition----------------
            Assert.IsTrue(dbColumn.Nullable.Value);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsFalse(boProp.Compulsory.GetValueOrDefault());
        }
        [Test]
        public void Test_UpdateValues_WhenDBColumnTinyInt1_ShouldUpdateToBoolean()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            IPropertyTypeStorer propertyTypeStorer = new PropertyTypeStorer(new DMSolution());
            IDBTypeToDMPropertyTypeMapper typeMapper = new DBTypeToDMPropertyTypeMapper(propertyTypeStorer);
            DBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(typeMapper);
            dbColumn.DataType = DBDataTypes.TINYINT;
            dbColumn.DataLength = 1;
            //---------------Assert Precondition----------------
            Assert.AreEqual(1, dbColumn.DataLength);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            StringAssert.Contains( "Boolean", boProp.PropertyTypeString);
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnNullableIsNull_ShouldUpdateCompulsoryToFalse()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            dbColumn.Nullable = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(dbColumn.Nullable);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsFalse(boProp.Compulsory.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnAutoIncrementFalse_ShouldUpdateAutoIncrementingToFalse()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.IsNotNull(dbColumn.IsAutoIncrementing);
            Assert.IsFalse(dbColumn.IsAutoIncrementing.Value);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsFalse(boProp.AutoIncrementing.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnAutoIncrementTrue_ShouldUpdateAutoIncrementingToTrue()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            dbColumn.IsAutoIncrementing = true;
            //---------------Assert Precondition----------------
            Assert.IsTrue(dbColumn.IsAutoIncrementing.Value);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsTrue(boProp.AutoIncrementing.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnIsAutoIncrementingIsNull_ShouldUpdateAutoIncrementingToFalse()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            dbColumn.IsAutoIncrementing = null;
            //---------------Assert Precondition----------------
            Assert.IsNull(dbColumn.IsAutoIncrementing);
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsFalse(boProp.AutoIncrementing.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenColumnNameDoesntContainPassword_ShouldUpdateKeepValuePrivateToFalse()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.IsFalse(dbColumn.ColumnName.Contains("Password"));
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsFalse(boProp.KeepValuePrivate.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenColumnNameContinsPassword_ShouldUpdateKeepValuePrivateToTrue()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldNamePasswordColumn");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsTrue(boProp.KeepValuePrivate.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenColumnNameContinsPassword_WhenVaryingCase_ShouldUpdateKeepValuePrivateToTrue()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldNamepaSsWoRdcolumn");
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            DMProperty boProp = new DMProperty();
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.IsTrue(boProp.KeepValuePrivate.GetValueOrDefault());
        }

        [Test]
        public void Test_UpdateValues_WhenOrdinalPositionIsNull_ShouldUpdateOrdinalPosition()
        {
            //---------------Set up test pack-------------------
            const int expectedOrdinalPos = 4;
            IDBColumn dbColumn = GetDBColumn("FieldName");
            dbColumn.OrdinalPosition = expectedOrdinalPos;
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            DMProperty boProp = new DMProperty();
            //---------------Assert Precondition----------------
            Assert.IsNull(boProp.OrdinalPosition);
            Assert.AreEqual(expectedOrdinalPos, dbColumn.OrdinalPosition);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedOrdinalPos, boProp.OrdinalPosition);
        }

        [Test]
        public void Test_UpdateValues_WhenOrdinalPositionIsNotNull_ShouldNotUpdateOrdinalPosition()
        {
            //---------------Set up test pack-------------------
            const int newOrdinalPos = 4;
            const int currentOrdinalPosition = 2;
            IDBColumn dbColumn = GetDBColumn("FieldName"); 
            dbColumn.OrdinalPosition = newOrdinalPos;
            DMProperty boProp = new DMProperty {OrdinalPosition = currentOrdinalPosition};
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.AreEqual(currentOrdinalPosition, boProp.OrdinalPosition);
            Assert.AreEqual(newOrdinalPos, dbColumn.OrdinalPosition);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(currentOrdinalPosition, boProp.OrdinalPosition);
        }

        [Test]
        public void Test_UpdateValues_WhenDBColumnHasDataType_ShouldSetCorrectPropertyType()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            dbColumn.DataType = TestUtilsShared.GetRandomString();
            DMPropertyType propertyType = new DMPropertyType();
            IDBTypeToDMPropertyTypeMapper typeMapper = CreateTypeMapperWithReturnPropertyType(dbColumn, propertyType);
            DBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(typeMapper);
            DMProperty boProp = new DMProperty();
            //---------------Assert Precondition---------------
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreSame(propertyType, boProp.PropertyType);
        }

        private static IDBTypeToDMPropertyTypeMapper CreateTypeMapperWithReturnPropertyType(IDBColumn dbColumn, DMPropertyType propertyType)
        {
            IDBTypeToDMPropertyTypeMapper typeMapper =
                MockRepository.GenerateStub<IDBTypeToDMPropertyTypeMapper>();
            typeMapper.Stub(t => t.GetDMPropertyType(dbColumn.DataType, dbColumn.DataLength)).Return(propertyType);
            return typeMapper;
        }

        [Test]
        public void Test_ImportAddedDBColumn_WhenPropertyTypeIsString_ShouldAddMaxLengthParameterToDefaultPropertyRule()
        {
            //---------------Set up test pack-------------------
            int dataLength = TestUtilsShared.GetRandomInt(50, 255);
            IDBColumn dbColumn = GetDBColumn("MyField");
            dbColumn.DataLength = dataLength;
            DMPropertyType propertyType = new DMPropertyType("System", "String");
            IDBTypeToDMPropertyTypeMapper typeMapper = CreateTypeMapperWithReturnPropertyType(dbColumn, propertyType);
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(typeMapper);
            DMProperty property = new DMProperty();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, property);
            //---------------Test Result -----------------------
            Assert.IsNotNull(property.DefaultPropRule);
            DMPropRuleParameter maxLengthParameter = property.DefaultPropRule.PropRuleParameters.Find(
                parameter => parameter.KeyName == "maxLength");
            Assert.IsNotNull(maxLengthParameter);
            Assert.AreEqual(dataLength.ToString(), maxLengthParameter.Value);
        }

        [Test]
        public void Test_UpdateValues_WhenLengthLT0_ShouldSetMaxLengthTo500_FIXBUG760()
        {
            //For SQL Server can have VarChar(Max) which results in DataLength being -1.
            //---------------Set up test pack-------------------

            IDBColumn dbColumn = GetDBColumn("MyField");

                dbColumn.DataLength = -1;

            DMPropertyType propertyType = new DMPropertyType("System", "String");
            IDBTypeToDMPropertyTypeMapper typeMapper = CreateTypeMapperWithReturnPropertyType(dbColumn, propertyType);
            IDBToDMPropertyUpdater dbToDMPropertyUpdater = new DBToDMPropertyUpdater(typeMapper);
            DMProperty property = new DMProperty();
            int expectedlength = 500;
            //---------------Assert Precondition----------------
            Assert.AreEqual(-1, dbColumn.DataLength);
            Assert.IsNull(property.DefaultPropRule);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, property);
            //---------------Test Result -----------------------
            Assert.IsNotNull(property.DefaultPropRule);
            DMPropRuleParameter maxLengthParameter =
                property.DefaultPropRule.PropRuleParameters.Find(parameter => parameter.KeyName == "maxLength");
            
            Assert.AreEqual(expectedlength.ToString(), maxLengthParameter.Value, "Max Length should be " + expectedlength);
        }

        [Test]
        public void Test_UpdateValues_whenPropNameNull_ShouldRenameToColumnName()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMProperty boProp = new DMProperty();
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.IsNull(boProp.PropertyName);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(dbColumn.ColumnName, boProp.PropertyName);
        }
        [Test]
        public void Test_UpdateValues_whenPropNameEmpty_ShouldRenameToColumnName()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            DMProperty boProp = new DMProperty {PropertyName = ""};
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.IsNullOrEmpty("", boProp.PropertyName);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(dbColumn.ColumnName, boProp.PropertyName);
        }
        [Test]
        public void Test_UpdateValues_whenPropNameNotEmpty_ShouldNotRenameColumnName()
        {
            //---------------Set up test pack-------------------
            IDBColumn dbColumn = GetDBColumn("FieldName");
            var initialPropertyName = RandomValueGen.GetRandomString();
            DMProperty boProp = new DMProperty {PropertyName = initialPropertyName};
            DBToDMPropertyUpdater dbToDMPropertyUpdater = CreateDBToDMPropertyUpdater();
            //---------------Assert Precondition----------------
            Assert.IsNullOrEmpty("", boProp.PropertyName);
            //---------------Execute Test ----------------------
            dbToDMPropertyUpdater.UpdateValues(dbColumn, boProp);
            //---------------Test Result -----------------------
            Assert.AreNotEqual(dbColumn.ColumnName, boProp.PropertyName);
            Assert.AreEqual(initialPropertyName, boProp.PropertyName);
        }


        protected virtual IDBColumn GetDBColumn()
        {
            return new DBColumn();
        }

        protected virtual IDBColumn GetDBColumn(string columnName)
        {
            return new DBColumn(columnName);
        }
    }
    [TestFixture]
    public class TestDbtoDMPropertyUpdater : TestDBToDMPropertyUpdater
    {
        protected override IDBColumn GetDBColumn()
        {
            return new DBViewColumn();
        }

        protected override IDBColumn GetDBColumn(string columnName)
        {
            return new DBViewColumn(columnName);
        }
    }
}